#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>

class Util
{
    enum STATUS_CODE
    {
        OK = 200,
        BAD_REQUEST = 400,
        NOT_FOUND = 404,
        SVR_ERROR = 500,
        SVR_UNAVL = 503
    };

public:
    static int ReadLine(int sock, std::string* out)
    {
        int cnt = 0;
        char ch = 'X';

        while (ch != '\n')
        {
            ssize_t s = recv(sock, &ch, 1, 0);
            if (s > 0)
            {
                if (ch == '\r')
                {
                    recv(sock, &ch, 1, MSG_PEEK); // 读到\r要窥探后面字符是否为\n
                    if (ch == '\n')
                        recv(sock, &ch, 1, 0); // 读走\n防止污染
                    else
                        ch = '\n'; // 说明行分隔符为\r，将\r设置为\n 直接结束循环
                }

                out->push_back(ch);
            }
            else if (s == 0) return  0;
            else             return -1;

            cnt += s;
        }

        return cnt;
    }

    static std::string GetTimeStamp()
    {
        struct timeval time_val;
        gettimeofday(&time_val, nullptr);
        return std::to_string(time_val.tv_sec);
    }

    static std::pair<std::string, std::string> GetKV(std::string& src, std::string sep)
    {
        size_t pos = src.find(sep);

        if (pos == std::string::npos)
            return std::make_pair("error", "error");

        return std::make_pair(src.substr(0, pos), src.substr(pos + sep.size()));
    }

    static bool CutString(const std::string& src, std::string* out1, std::string* out2, std::string sep)
    {
        size_t pos = src.find(sep);
        if (pos == std::string::npos)
            return false;

        *out1 = std::move(src.substr(0, pos));
        *out2 = std::move(src.substr(pos + sep.size()));

        return true;
    }

    static bool CutString(const std::string& src, std::string* out1, std::string* out2, size_t pos)
    {
        if (pos == std::string::npos)
            return false;

        *out1 = std::move(src.substr(0, pos));
        *out2 = std::move(src.substr(pos + 1));

        return true;
    }

    static std::string Code2Desc(int code)
    {
        switch (code)
        {
        case OK:
            return "OK";
        case BAD_REQUEST:
            return "Bad Request";
        case NOT_FOUND:
            return "Not Found";
        case SVR_ERROR:
            return "Sever Error";
        case SVR_UNAVL:
            return "Sever Unavailable";
        default:
            return "Unkown Http Status code";
        }
    }

    static std::string GetSuffix(std::string path)
    {
        size_t pos = path.rfind('.');

        if (pos == std::string::npos)
            return ".html";

        return path.substr(pos);
    }

    static std::string Suffix2Type(std::string&& suffix)
    {
        auto it = _suffix_type_map.find(suffix);

        if (it == _suffix_type_map.end())
            return "text/html";

        return it->second;
    }


private:
    static std::unordered_map<std::string, std::string> _suffix_type_map;

};

std::unordered_map<std::string, std::string> Util::_suffix_type_map = {
    {".html","text/html"},
    {".htm","text/html"},
    {".css", "text/css"},
    {".js", "application/javascript"},
    {".txt","text/plain"},
    {".xml", "application/xml"},
    {".pdf","application/pdf"},
    {".png", "image/png"},
    {".jpg", "image/jpg"},
    {".gif", "image/gif"},
    {".svg", "image/svg+xml"},
    {".mp3", "audio/mp3"},
    {".mp4", "video/mpeg4"},
    {".exe","application/x-msdownload"},
    {".woff", "application/font-woff"}
};